-----Computer Drill and Instruction----
--------------Decimals C-2-------------
A 4am crack                  2017-10-16
---------------------------------------

Name: Computer Drill and Instruction:
  Decimals C-2
Genre: educational
Year: 1981
Publisher: Science Research Associates
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: DOS 3.2
Previous cracks: none

                   ~

               Chapter 0
     If You Give a Cracker a Disk


This disk was automatically cracked by
the development version of Passport
(dated 2017-10-01). Here is the
transcript:

                 --v--

READING FROM S6,D1
T00,S00 FOUND DOS 3.2 BOOTLOADER
USING DISK'S OWN RWTS
WRITING TO S5,D2
T00,S02 WRITING BUILT-IN RWTS
T00,S01,$38: 20 -> 2C
T00,S00 WRITING STANDARD DELIVERY
BOOTLOADER
CRACK COMPLETE.

                 --^--

[Narrator]
But the crack was not complete.

Passport successfully converts the
original disk from DOS 3.2 to DOS 3.3.
But when I boot the copy, it gets as
far as displaying part of the title
screen before displaying this message:

                 --v--

YOU HAVE ENCOUNTERED ERROR NO. CDIM-592
PLEASE CONTACT SRA IN CHICAGO FOR
ASSISTANCE.

                 --^--

Pro-tip: do not do that.

                   ~

               Chapter 1
     If You Give a Pirate a Prompt


After displaying the cryptic error
message, my non-working copy drops me
to a BASIC prompt where any attempt to
LIST the current program in memory RUNs
it instead.

This is what it looks like when you set
the RUN flag, zero page $D6. However,
you can just as easily clear the flag
by re-initializing BASIC with the "FP"
command.

]FP

It appears that DOS is still in memory.
Let's see if I can use that to my
advantage.

]CATALOG

DISK VOLUME 254

 A 003 HELLO
 B 040 MPG CODE
 A 016 MINI LOADER
 A 021 MINI P1
 A 023 MINI P12
 A 021 MINI P3
 A 012 STUDENT
 A 008 SIGNON
 A 017 HOME
 B 005 FONT2
 B 012 FONT1
 B 002 C-DECC
 B 005 DECC37
 B 006 DECC24
 B 011 HDECC24
 B 008 DECC25
 B 009 HDECC25
 B 005 DECC30
 B 006 DECC33
 B 007 DECC51
 B 011 HDECC51
 B 003 DECC27
 B 007 HDECC27
 B 003 DECC28
 B 007 HDECC28
 B 005 DECC34
 B 005 DECC71
 B 009 HDECC71
 B 002 PL-DECC
 B 010 HDECC30
 B 006 HDECC37
 B 002 RNDM
 B 005 DECC38
 B 005 DECC39
 B 010 HDECC34
 B 009 HDECC54
 B 010 DECC54
 B 004 DECC43
 B 006 HDECC43
 B 002 Q-DECC

]LOAD HELLO
]LIST

 1  REM  HELLO VER:1.1/RPT
 2  REM  COPYRIGHT 1981 SRA. ALL
     RIGHTS RESERVED. LICENSED MA
     TERIAL. PROGRAM PROPERTY OF
     SRA.
 5 D$ =  CHR$ (4)
 10  GOSUB 100
 12  POKE 1010,0: POKE 1011,0
 15  PRINT D$
 20  POKE 103,1
 25  POKE 104,67
 30  POKE 17152,0
 40  POKE 214,227
 45  POKE 215,55
 50  PRINT D$;"RUN STUDENT"
 100  IF  PEEK (116) = 150 THEN  RETURN

 110  HOME : PRINT : PRINT "THE C
     OMPUTER MUST HAVE 48K OF MEM
     ORY TO OPERATE CDIM.": NEW

The subroutine at line 100 checks that
DOS relocated properly to higher memory
during boot. Line 12 munges the reset
vector. Lines 20 and 25 set a custom
start address in memory for the next
BASIC program. Line 40 sets the RUN
flag I noted earlier.

Nothing extraordinary yet.

Then it runs another program, STUDENT.

                   ~

               Chapter 2
     If You Give a STUDENT a CALL


]LOAD STUDENT
]LIST

 1  REM  STUDENT VER:1.1/FRAC-2/L
     L
 2  REM  COPYRIGHT 1981 SRA. ALL
     RIGHTS RESERVED. LICENSED MA
     TERIAL. PROGRAM PROPERTY OF
     SRA.
 3  ONERR  GOTO 60000
 10  POKE 30840,0: POKE 30841,141
     : REM  TRAIL STACK
 15  GOSUB 8000
 16 Q =  RND ( - 1)
 18  HCOLOR= 3
 20  HOME
 21  PRINT "COPYRIGHT SRA 1981.":
      PRINT "ALL RIGHTS RESERVED.
     ": PRINT "LICENSED MATERIAL.
     ": PRINT "PROGRAM PROPERTY O
     F SRA.": PRINT "PRODUCED IN
     USA."
 22  PRINT : PRINT : PRINT : PRINT

 23  PRINT "THE COPYRIGHTED COMPU
     TER PROGRAMS AND": PRINT "TE
     XT MATERIAL CONTAINED HEREIN
      ARE": PRINT "FURNISHED UNDE
     R LICENSE FROM SRA."
 24  PRINT "THE RIGHTS TO THIS MA
     TERIAL ARE": PRINT "OWNED OR
      CONTROLLED BY SRA AND MAY":
      PRINT "BE USED ONLY IN ACCO
     RDANCE WITH THE"
 25  PRINT "PROVISIONS DETAILED I
     N THE LICENSE": PRINT "BETWE
     EN THE CUSTOMER AND SRA."
 30  POKE  - 16368,0

OK, I saw all of that printed on my
non-working copy.

 40  PRINT  CHR$ (4);"BLOAD MPG C
     ODE"
 41  PRINT  CHR$ (4);"BLOAD FONT1
     "
 42  PRINT  CHR$ (4);"BLOAD FONT2
     "

Looks like we're loading several
binary files. Some sort of hi-res
character generator?

 43  POKE 49289 +  PEEK (47081),2
     55
 45  GOSUB 9300: GOSUB 100: GOSUB
     200

]LIST 9300

9300  HOME : HGR :X% = 0:Y% = 15
     : POKE  - 16302,0:X% = 0:Y% =
     15:FY% = 0: RETURN

OK, that just initializes the hi-res
screen and sets some global variables.

]LIST 100,

 100  FOR X = 8 TO 28 STEP 5: HPLOT
     X,90 TO X - 3,90 TO X - 3,12
     0 TO X,120 TO X,90:: NEXT X
 102  HPLOT 30,90 TO 30,120 TO 60
     ,120 TO 60,90 TO 30,90
 110  FOR Y = 8 TO 88 STEP 5: HPLOT
     64,Y TO 94,Y TO 94,Y - 3 TO
     64,Y - 3 TO 64,Y: NEXT Y
 111  HPLOT 64,90 TO 94,90 TO 94,
     120 TO 64,120 TO 64,90
 120  FOR X = 273 TO 133 STEP  -
     5: HPLOT X,90 TO X,120 TO X -
     3,120 TO X - 3,90 TO X,90: NEXT
     X
 121  HPLOT 98,90 TO 128,90 TO 12
     8,120 TO 98,120 TO 98,90
 130  HCOLOR= 3
 131  HPLOT 53,97 TO 50,95 TO 37,
     95 TO 35,97 TO 35,103 TO 37,
     105 TO 50,105
 132  HPLOT 50,105 TO 53,107 TO 5
     3,113 TO 50,115 TO 37,115 TO
     35,112
 139  FOR I = 1 TO 500: NEXT I
 141  HPLOT 71,115 TO 71,95 TO 87
     ,95 TO 89,97 TO 89,103 TO 87
     ,105 TO 71,105
 142  HPLOT 81,105 TO 85,109 TO 8
     8,115
 149  FOR I = 1 TO 500: NEXT I
 151  HPLOT 104,115 TO 113,95 TO
     123,115
 152  HPLOT 106,109 TO 120,109
 153 X% = 121:Y% = 87: REM  X AND
      Y COOR FOR *
 154  GOSUB 180: REM  PRINT ASTER
     ISK

We're drawing a bunch of stuff on the
screen. I saw all of this on my non-
working copy.

]LIST 180,

 180  HPLOT X%,Y% TO X% + 4,Y%
 181  HPLOT X% + 1,Y% + 2 TO X% +
     3,Y% - 2
 182  HPLOT X% + 1,Y% - 2 TO X% +
     3,Y% + 2
 183  RETURN

More manual drawing.

Continuing after line 154...

]LIST 155,

 160 X% = 16:Y% = 30:A$ = "]COMPU
     TER": GOSUB 9900

]LIST 9900,

 9900  REM  --DISPLAY HI-RES TEXT

 9901 OX% = X%: GOSUB 9950:X% = O
     X%:Y% = Y% + 15:NX% =  FRE (
     0): RETURN
 9950  FOR II = 1 TO  LEN (A$): POKE
     30800 + II - 1, ASC ( MID$ (
     A$,II,1)): NEXT : POKE 31340
     , LEN (A$)
 9952  POKE 31355,X%: POKE 30894,
     Y%: POKE 30897,S%
 9953  POKE 30964,69
 9954  CALL 23601
 9956  RETURN

Ah, I was right. Those binary files
we loaded earlier are some sort of
hi-res character generator. We're
POKE-ing the strings into memory, one
byte at a time, then CALL-ing the print
routine on line 9954.

But here's the really interesting part:
this is where my non-working copy
stopped working. It never prints the
string "COMPUTER" in any hi-res font.
The original disk does. So I think we
are never returning from that CALL on
line 9954.

23601 is $5C31 in hex, so let's BLOAD
those files and see what's going on.

                   ~

               Chapter 3
      If You Give an Apple a Byte


]BLOAD MPG CODE
]BLOAD FONT1
]BLOAD FONT2

*5C31L

5C31-   A9 00       LDA   #$00
5C33-   20 00 7B    JSR   $7B00
5C36-   8D 6C 7A    STA   $7A6C

*7B00L

7B00-   20 0F 5A    JSR   $5A0F

*5A0FL

5A0F-   AC 6C 7A    LDY   $7A6C
5A12-   99 50 78    STA   $7850,Y
5A15-   C8          INY
5A16-   8C 6C 7A    STY   $7A6C
5A19-   60          RTS

Continuing from $7B03...

7B03-   20 1F 7C    JSR   $7C1F

*7C1FL

7C1F-   A2 FF       LDX   #$FF
7C21-   18          CLC
7C22-   3E 0F 7B    ROL   $7B0F,X
7C25-   CA          DEX
7C26-   D0 FA       BNE   $7C22
7C28-   60          RTS

Ah, decrypting the code in the caller!

*7C1FG

Continuing from $7B06...

; Overwrite the code that called this
; routine in the first place. The next
; time something CALLs this program,
; it will simply do its thing instead
; of redirecting to this protection
; check!
7B06-   A9 0F       LDA   #$0F
7B08-   8D 34 5C    STA   $5C34
7B0B-   A9 5A       LDA   #$5A
7B0D-   8D 35 5C    STA   $5C35

Everything after this was previously
encrypted, but the subroutine at $7C1F
decrypted it.

*7B10L

; turn on drive motor manually
7B10-   AE E9 B7    LDX   $B7E9
7B13-   BD 89 C0    LDA   $C089,X

; wait for the drive to spin up
7B16-   A9 FF       LDA   #$FF
7B18-   20 A8 FC    JSR   $FCA8
7B1B-   A9 FF       LDA   #$FF
7B1D-   20 A8 FC    JSR   $FCA8
7B20-   A9 FF       LDA   #$FF
7B22-   20 A8 FC    JSR   $FCA8
7B25-   A9 FF       LDA   #$FF
7B27-   20 A8 FC    JSR   $FCA8

; clear all status flags
7B2A-   A9 00       LDA   #$00
7B2C-   48          PHA
7B2D-   28          PLP
7B2E-   D0 01       BNE   $7B31
7B30-   4C 20 E3    JMP   $E320
7B33-   03          ???

Fake. Since we just forcibly cleared
all the status flags, including the Z
flag, the "BNE" at $7B2E will always
branch... into the middle of the next
instruction!

*7B31L

7B31-   20 E3 03    JSR   $03E3
7B34-   85 01       STA   $01
7B36-   84 00       STY   $00
7B38-   A0 01       LDY   #$01
7B3A-   D0 01       BNE   $7B3D
7B3C-   D0 B1       BNE   $7AEF
7B3E-   00          BRK

Fake. The "BNE" at $7B3A will always
branch, because we just set Y=1.
Execution continues at $7B3D, which is
in the middle of an instruction again.

I'll spare you the blow-by-blow, but
there are a number of these. 14. There
are 14 of these.

*7B30:EA
*7B3C:EA
*7B42:EA
*7B4C:EA
*7B52:EA
*7B5B:EA
*7B63:EA
*7B6A:EA
*7B6D:EA
*7B77:EA
*7B7F:EA
*7B86:EA
*7B8E:EA
*7B95:EA

Whew.

                   ~

               Chapter 4
      If You Give a Nibble a Bit


And here is the entire copy protection
routine, as unobfuscated as it can get:

*7B10L

; turn on drive motor manually
7B10-   AE E9 B7    LDX   $B7E9
7B13-   BD 89 C0    LDA   $C089,X

; wait for the drive to spin up
7B16-   A9 FF       LDA   #$FF
7B18-   20 A8 FC    JSR   $FCA8
7B1B-   A9 FF       LDA   #$FF
7B1D-   20 A8 FC    JSR   $FCA8
7B20-   A9 FF       LDA   #$FF
7B22-   20 A8 FC    JSR   $FCA8
7B25-   A9 FF       LDA   #$FF
7B27-   20 A8 FC    JSR   $FCA8

; clear all status flags
7B2A-   A9 00       LDA   #$00
7B2C-   48          PHA
7B2D-   28          PLP
7B2E-   D0 01       BNE   $7B31
7B30-   EA          NOP

; get address of RWTS parameter table
7B31-   20 E3 03    JSR   $03E3
7B34-   85 01       STA   $01
7B36-   84 00       STY   $00
7B38-   A0 01       LDY   #$01
7B3A-   D0 01       BNE   $7B3D
7B3C-   EA          NOP

; get current slot from RWTS table
7B3D-   B1 00       LDA   ($00),Y
7B3F-   AA          TAX
7B40-   D0 01       BNE   $7B43
7B42-   EA          NOP

; turn on drive motor manually (again)
7B43-   BD 89 C0    LDA   $C089,X

; set up Death Counter
7B46-   A9 56       LDA   #$56
7B48-   85 00       STA   $00
7B4A-   D0 01       BNE   $7B4D
7B4C-   EA          NOP
7B4D-   88          DEY
7B4E-   D0 0C       BNE   $7B5C
7B50-   F0 01       BEQ   $7B53
7B52-   EA          NOP
7B53-   C6 00       DEC   $00
7B55-   D0 05       BNE   $7B5C

All roads lead to $7B5C until the Death
Counter in zp$00 hits 0.

; failure path branches to $7B96
7B57-   A9 00       LDA   #$00
7B59-   F0 3B       BEQ   $7B96
7B5B-   EA          NOP

; execution continues here --
; find a $BF nibble
7B5C-   BD 8C C0    LDA   $C08C,X
7B5F-   10 FB       BPL   $7B5C
7B61-   D0 01       BNE   $7B64
7B63-   EA          NOP
7B64-   C9 BF       CMP   #$BF
7B66-   F0 03       BEQ   $7B6B
7B68-   D0 E3       BNE   $7B4D
7B6A-   EA          NOP
7B6B-   F0 01       BEQ   $7B6E
7B6D-   EA          NOP

; Read data latch exactly once (no BPL
; loop here!) and check its value.
; We're out of sync here because of all
; the branching, so the exact value of
; the data latch depends on timing bit
; after the $BF nibble.
7B6E-   BD 8C C0    LDA   $C08C,X
7B71-   C9 08       CMP   #$08
7B73-   B0 D8       BCS   $7B4D
7B75-   D0 01       BNE   $7B78
7B77-   EA          NOP

; find a $D5 nibble
7B78-   BD 8C C0    LDA   $C08C,X
7B7B-   10 FB       BPL   $7B78
7B7D-   D0 01       BNE   $7B80
7B7F-   EA          NOP
7B80-   C9 D5       CMP   #$D5
7B82-   F0 03       BEQ   $7B87
7B84-   D0 C7       BNE   $7B4D
7B86-   EA          NOP

; find an $AA nibble
7B87-   BD 8C C0    LDA   $C08C,X
7B8A-   10 FB       BPL   $7B87
7B8C-   D0 01       BNE   $7B8F
7B8E-   EA          NOP
7B8F-   C9 AA       CMP   #$AA
7B91-   F0 03       BEQ   $7B96
7B93-   D0 B8       BNE   $7B4D
7B95-   EA          NOP

; both success and failure paths lead
; here --
; turn off drive motor
7B96-   DD 88 C0    CMP   $C088,X

; check current nibble under the drive
; head (will only be $AA if we got here
; through the success path)
7B99-   C9 AA       CMP   #$AA

; branch on success
7B9B-   F0 13       BEQ   $7BB0

; failure path falls through to here --
; switch to text page and display error
; message
7B9D-   AD 51 C0    LDA   $C051
7BA0-   AD 54 C0    LDA   $C054
7BA3-   A2 00       LDX   #$00
7BA5-   BD B3 7B    LDA   $7BB3,X
7BA8-   F0 66       BEQ   $7C10
7BAA-   20 ED FD    JSR   $FDED
7BAD-   E8          INX
7BAE-   D0 F5       BNE   $7BA5
7BB0-   A9 00       LDA   #$00
7BB2-   60          RTS
7BB3-   "YOU HAVE ENCOUNTERED ERROR..."

; execution continues here (from $7BA8)
; after it finishes displaying the
; error message
7C10-   20 16 7C    JSR   $7C16

*7C16L

; re-encrypt protection check in memory
7C16-   A2 00       LDX   #$00
7C18-   7E 10 7B    ROR   $7B10,X
7C1B-   E8          INX
7C1C-   D0 FA       BNE   $7C18
7C1E-   60          RTS

Continuing from $7C13...

; exit via DOS warm start
7C13-   4C D0 03    JMP   $03D0

...which is the behavior I saw on my
non-working copy.

                   ~

               Chapter 5
     If You Give a Caller a Bypass


To sum up:

This hi-res character generator has a
built-in protection check that looks
for the nibble sequence

  BF+ D5 AA

where "+" is a timing bit.

Nowhere in this routine does it seek to
any specific track. Inspecting the
original disk with a nibble editor, I
see this nibble sequence at the start
of every track! Just before the address
prologue of sector 0, there is a $BF
nibble with a timing bit.

The protection check is only run once.
As soon as it runs, it overwrites the
calling code at $5C33 so future calls
will bypass the protection check. That
points to an obvious solution: bypass
the protection check in exactly that
way, but permanently.

Turning to my trusty Disk Fixer sector
editor, I search the disk for the JSR
that called $7B00 ("20 00 7B") and find
it on track $1A, sector $05 -- part of
the "MPG CODE" file.

T1A,S05,$3B: 007B -> 0F5A

]PR#6
...works...

                   ~

               Chapter 6
 ...He's Going to Want to Automate It


I'm not sure if this was intentional,
but the magic nibble sequence can be
integrated into either DOS 3.2 or 3.3
disks. After the $BF+bit, the next two
nibbles are the start of the address
prologue ($D5 $AA), which are unchanged
between DOS 3.2 and 3.3.

I have six other SRA disks that use an
identical protection scheme -- some DOS
3.2, others DOS 3.3. It seems that SRA
reused this protection scheme across
their product line for several years.
And you know what that means...

Automation!

The next version of Passport will
detect and defeat this protection. Re-
running the updated Passport on this
disk, the crack log looks like this:

                 --v--

READING FROM S6,D1
T00,S00 FOUND DOS 3.2 BOOTLOADER
USING DISK'S OWN RWTS
WRITING TO S5,D2
T1A,S05 FOUND SRA PROTECTION CHECK
T1A,S05,$3B: 007B -> 0F5A
T00,S02 WRITING BUILT-IN RWTS
T00,S01,$38: 20 -> 2C
T00,S00 WRITING STANDARD DELIVERY
BOOTLOADER
CRACK COMPLETE.

                 --^--

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1477
------------------EOF------------------
